package cn.fivestars.springcloud.order.lb;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 通过实现接口来实现自定义负载均衡算法
 *
 * @author liangchenzhou1024@163.com
 * @date 2021/2/4
 */
@Slf4j
public class MyRibbonLoadBalancer extends AbstractLoadBalancerRule {
    private final AtomicInteger atomicInteger = new AtomicInteger(0);

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    private Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }

        Server server;
        int count = 0;
        while (count++ < 10) {

            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }

            int index = getAndIncrement() % serverCount;
            server = allServers.get(index);

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }

            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return null;
    }

    protected final int getAndIncrement() {
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = (current == 127 ? 0 : current + 1);
        } while (!this.atomicInteger.compareAndSet(current, next));
        log.info("访问次数:next={}", next);
        return next;
    }
}
